package projet.volleystat.automate;

import java.util.ArrayList;
import java.util.Vector;

import projet.volleystat.Team;

/**
 * @author Lameira Nelaupe Ferrand Fiore
 * @version 1.0
 * Cette classe modelise l'automate.
 * Elle emet des signaux qui sont decrit dans l'interface EtatListener.
 * Elle donne acces � une methode avancerEtat, qui permet de generer de nouvel �tat grace � une action.
 * @see avancerEtat(ActionVolley action)
 */
public class VolleyStateMachine {

	private StateListener stateListener;
	private Vector<VolleyAction> actionHistoric;
	private Vector<VolleyState> stateHistoric;
	private Team defTeam;
	private Team oppTeam;

	/**
	 * @param eqDef
	 * @param eqAdv
	 * @param listener
	 * Le constructeur de cette automate initialise les variables.
	 * il permet d'implementer, dans une classe mere, les actions � faire lors d'emission de signal.
	 */
	public VolleyStateMachine(Team eqDef, Team eqAdv, StateListener listener) {
		super();
		this.stateListener = listener;
		this.defTeam = eqDef;
		this.oppTeam = eqAdv;
		this.stateHistoric = new Vector<VolleyState>();
		this.actionHistoric = new Vector<VolleyAction>();
	}

	/**
	 * @return Vector<VolleyAction>
	 * Recupere la liste des action d'un match.
	 */
	public Vector<VolleyAction> getActionHistoric() {
		return actionHistoric;
	}

	/**
	 * @param eqEngageante
	 * Creer l'�tat initial.
	 * eqEngageante designe l'equipe eyant le premier service du match. 
	 */
	public void createFirstState(Team eqEngageante){
		stateHistoric.add(new VolleyState(defTeam, oppTeam, eqEngageante));
	}

	/**
	 * @return EtatVolley
	 * Renvoie l'�tat dans lequel l'automate se trouve.
	 */
	public VolleyState currentState(){
		return stateHistoric.lastElement();
	}

	/**
	 * Retourne un état en arriere.
	 */
	public void back(){
		if(!actionHistoric.isEmpty()){
			actionHistoric.remove(actionHistoric.size()-1);
			stateHistoric.remove(stateHistoric.size()-1);
		}
	}

	/**
	 * @param action
	 * @throws VolleyStateMachineException
	 * Genere un nouvelle �tat deduis de l'�tat en cours et d'une action.
	 * Cette fonction emet des signaux.
	 */
	public void generateState(VolleyAction action) throws VolleyStateMachineException{
		VolleyState volleyState = stateHistoric.lastElement();
		ArrayList<Integer> allowedAction;
		int valueSwitch = action.getId();
		if(valueSwitch < 30){
			valueSwitch = 0;
		}
		if(valueSwitch == 37){
			if(!actionHistoric.isEmpty()){
				switch (actionHistoric.lastElement().getId()){
				case 32:
					((PassVolleyAction)action).setAction(PassVolleyAction.Action.Reception);
					actionHistoric.lastElement().setMark(action.getMark());
					valueSwitch=35;
					break;
				default:
					((PassVolleyAction)action).setAction(PassVolleyAction.Action.Pass);
					valueSwitch=36;
					break;
				}
			}
		}
		if(!volleyState.isPermitted(valueSwitch)){
			throw new VolleyStateMachineException(VolleyStateMachineException.Reason.forbiddenAction);
		}
		allowedAction = new ArrayList<Integer>();
		switch(valueSwitch){
		case 0:
			/* ------- Debut Faute Match ------- */
			volleyState = stateHistoric.lastElement().duplicate();
			allowedAction.add(31);
			allowedAction.add(32);
			volleyState.defineAllowedAction(allowedAction);
			if(action.getTeam() == null && action.getPlayer() == null){
				if(!actionHistoric.isEmpty()){
					action.setTeam(actionHistoric.lastElement().getTeam());
					action.setPlayer(actionHistoric.lastElement().getPlayer());
				}
			}
			if(volleyState.getCurrentTeam() == action.getTeam()){
				if(action.getTeam() == defTeam){
					volleyState.addPointToOppTeam();
					volleyState.oppTeamRotate();
					volleyState.setCurrentTeam(oppTeam);
					actionHistoric.add(action);
					stateHistoric.add(volleyState);
					stateListener.oppTeamRotationSignal();
					if(VolleyState.isMatchEnded()){
						stateListener.matchEndingSignal(oppTeam);
					}
				}
				else{
					volleyState.addPointToDefTeam();
					volleyState.defTeamRotate();
					volleyState.setCurrentTeam(defTeam);
					actionHistoric.add(action);
					stateHistoric.add(volleyState);
					stateListener.defTeamRotationSignal();
					if(VolleyState.isMatchEnded()){
						stateListener.matchEndingSignal(defTeam);
					}
				}
			}
			else{
				if(action.getTeam() == defTeam){
					volleyState.addPointToOppTeam();
					volleyState.setCurrentTeam(oppTeam);
					actionHistoric.add(action);
					stateHistoric.add(volleyState);
					if(VolleyState.isMatchEnded()){
						stateListener.matchEndingSignal(oppTeam);
					}
				}
				else{
					volleyState.addPointToDefTeam();
					volleyState.setCurrentTeam(defTeam);
					actionHistoric.add(action);
					stateHistoric.add(volleyState);
					if(VolleyState.isMatchEnded()){
						stateListener.matchEndingSignal(defTeam);
					}
				}
			}
			stateListener.faultSignal(action.getFaultId());
			break;
			/* ------- Fin Faute Match ------- */
		case 30:
			/* ------- Debut Point Marque ------- */
			volleyState = stateHistoric.lastElement().duplicate();
			allowedAction.add(31);
			allowedAction.add(32);
			volleyState.defineAllowedAction(allowedAction);
			if(action.getTeam() == null && action.getPlayer() == null){
				if(!actionHistoric.isEmpty()){
					action.setTeam(actionHistoric.lastElement().getTeam());
					action.setPlayer(actionHistoric.lastElement().getPlayer());
				}
			}
			if(action.getTeam().haveTwo(action.getPlayer(), ((MarkedPointVolleyAction)action).getTargetPlayer())){
				// Un joueur a marque contre son camp
				if(action.getTeam() == volleyState.getCurrentTeam()) {
					if(action.getTeam() == defTeam){
						volleyState.addPointToOppTeam();
						volleyState.oppTeamRotate();
						volleyState.setCurrentTeam(oppTeam);
						actionHistoric.add(action);
						stateHistoric.add(volleyState);
						stateListener.oppTeamRotationSignal();
					}
					else{
						volleyState.addPointToDefTeam();
						volleyState.defTeamRotate();
						volleyState.setCurrentTeam(defTeam);
						actionHistoric.add(action);
						stateHistoric.add(volleyState);
						stateListener.defTeamRotationSignal();
					}
				}
				else {
					if(action.getTeam() == defTeam){
						volleyState.addPointToOppTeam();
						actionHistoric.add(action);
						stateHistoric.add(volleyState);
					}
					else{
						volleyState.addPointToDefTeam();
						actionHistoric.add(action);
						stateHistoric.add(volleyState);
					}
				}
			}
			else {
				// Un joueur a marque dans le camp adverse
				if(action.getTeam() == volleyState.getCurrentTeam()) {
					if(action.getTeam() == defTeam){
						volleyState.addPointToDefTeam();
						actionHistoric.add(action);
						stateHistoric.add(volleyState);
					}
					else{
						volleyState.addPointToOppTeam();
						actionHistoric.add(action);
						stateHistoric.add(volleyState);
					}
				}
				else {
					if(action.getTeam() == defTeam){
						volleyState.addPointToDefTeam();
						volleyState.defTeamRotate();
						volleyState.setCurrentTeam(defTeam);
						actionHistoric.add(action);
						stateHistoric.add(volleyState);
						stateListener.defTeamRotationSignal();
					}
					else{
						volleyState.addPointToOppTeam();
						volleyState.oppTeamRotate();
						volleyState.setCurrentTeam(oppTeam);
						actionHistoric.add(action);
						stateHistoric.add(volleyState);
						stateListener.oppTeamRotationSignal();
					}
				}
			}
			if(VolleyState.isMatchEnded()){
				stateListener.matchEndingSignal(action.getTeam());
			}
			stateListener.markedPointSignal(action.getTeam());
			break;
			/* ------- Fin Point Marque ------- */
		case 31:
			/* ------- Debut Changement ------- */
			volleyState = stateHistoric.lastElement().duplicate();
			if(action.getTeam() == defTeam){
				VolleyStateMachineException.Reason reason =
						volleyState.defTeamReplacePlayer(action.getPlayer(), ((ReplacementVolleyAction)action).getReplacingPlayer());
				if(reason == null){
					allowedAction.add(31);
					allowedAction.add(32);
					volleyState.defineAllowedAction(allowedAction);
					actionHistoric.add(action);
					stateHistoric.add(volleyState);
					stateListener.playerReplacingSignal(action.getPlayer(), ((ReplacementVolleyAction)action).getReplacingPlayer());
				}
				else {
					throw new VolleyStateMachineException(reason);
				}
			}
			else{
				VolleyStateMachineException.Reason reason =
						volleyState.oppTeamReplacePlayer(action.getPlayer(), ((ReplacementVolleyAction)action).getReplacingPlayer());
				if(reason == null){
					allowedAction.add(31);
					allowedAction.add(32);
					volleyState.defineAllowedAction(allowedAction);
					actionHistoric.add(action);
					stateHistoric.add(volleyState);
					stateListener.playerReplacingSignal(action.getPlayer(), ((ReplacementVolleyAction)action).getReplacingPlayer());
				}
				else {
					throw new VolleyStateMachineException(reason);
				}
			}
			break;
			/* ------- Fin Changement ------- */
		case 32:
			/* ------- Debut Service  ------- */
			volleyState = stateHistoric.lastElement().duplicate();
			allowedAction.add(0);
			allowedAction.add(30);
			allowedAction.add(35);
			volleyState.defineAllowedAction(allowedAction);
			if(action.getTeam() == null && action.getPlayer() == null){
				action.setTeam(volleyState.getCurrentTeam());
				action.setPlayer(volleyState.getServicePlayer());
			}
			actionHistoric.add(action);
			stateHistoric.add(volleyState);
			stateListener.serviceSignal(action.getFaultId());
			break;
			/* ------- Fin Service  ------- */
		case 35:
			/* ------- Debut Reception ------- */
			volleyState = stateHistoric.lastElement().duplicate();
			allowedAction.add(0);
			allowedAction.add(30);
			allowedAction.add(36);
			volleyState.defineAllowedAction(allowedAction);
			actionHistoric.add(action);
			stateHistoric.add(volleyState);
			stateListener.receptionSignal(action.getPlayer());
			break;
			/* ------- Fin Reception ------- */
		case 36:
			/* ------- Debut Passe ------- */
			actionHistoric.add(action);
			stateHistoric.add(volleyState);
			stateListener.passSignal(action.getPlayer());
			break;
			/* ------- Fin Passe ------- */
		}
	}
}
